package com.framework.test.listeners;

import com.aventstack.extentreports.ExtentReports;
import com.aventstack.extentreports.ExtentTest;
import com.aventstack.extentreports.Status;
import com.aventstack.extentreports.reporter.ExtentSparkReporter;
import com.aventstack.extentreports.reporter.configuration.Theme;
import com.framework.test.utils.PathConfigUtil;
import org.testng.ITestContext;
import org.testng.ITestListener;
import org.testng.ITestResult;
import org.testng.Reporter;

import java.util.List;

public class ExtentReportListener implements ITestListener {
    private ExtentReports extent;
    private ExtentTest test;

    private static ExtentReports getInstance() {
        ExtentReports extent = new ExtentReports();
        // 使用时间戳生成报告文件名
        String timestamp = System.getProperty("current.timestamp");
        if (timestamp == null) {
            timestamp = System.getProperty("current.date", "unknown");
        }
        String reportFileName = "ExecutionReport_" + timestamp + ".html";
        String fileSeparator = System.getProperty("file.separator");
        String reportFilePath = PathConfigUtil.getReportsPath();
        String reportFileLocation = reportFilePath + reportFileName;

        ExtentSparkReporter htmlReporter = new ExtentSparkReporter(reportFileLocation);
        htmlReporter.config().setDocumentTitle(reportFileName);
        htmlReporter.config().setReportName(reportFileName);
        htmlReporter.config().setTheme(Theme.STANDARD);
        htmlReporter.config().setEncoding("utf-8");
        htmlReporter.config().setTimeStampFormat("EEEE, MMMM dd, yyyy, hh:mm a '('zzz')'");
        htmlReporter.config().setResourceCDN("EXTENTREPORTS");

        extent.attachReporter(htmlReporter);
        extent.setSystemInfo("OS", System.getProperty("os.name"));
        extent.setSystemInfo("Java Version", System.getProperty("java.version"));
        return extent;
    }

    @Override
    public void onStart(ITestContext context) {
        extent = getInstance();
    }

    @Override
    public void onTestStart(ITestResult result) {
        Object[] params = result.getParameters();
        String testName = result.getMethod().getMethodName();
        String className = result.getTestClass().getName();
        
        // 构建更详细的测试名称
        StringBuilder testDisplayName = new StringBuilder();
        testDisplayName.append(className.substring(className.lastIndexOf('.') + 1))
                      .append(".").append(testName);
        
        if (params.length > 0) {
            if (params[0] instanceof String) {
                testDisplayName.append(".").append((String) params[0]);
            } else {
                testDisplayName.append(" [Parameters: ");
                for (int i = 0; i < Math.min(params.length, 3); i++) {
                    if (i > 0) testDisplayName.append(", ");
                    testDisplayName.append(params[i] != null ? params[i].toString() : "null");
                }
                if (params.length > 3) {
                    testDisplayName.append(", ...");
                }
                testDisplayName.append("]");
            }
        }

        test = extent.createTest(testDisplayName.toString());
        test.assignCategory(className.substring(className.lastIndexOf('.') + 1));
    }

    @Override
    public void onTestSuccess(ITestResult result) {
        long duration = result.getEndMillis() - result.getStartMillis();
        test.log(Status.PASS, String.format("测试通过 (执行时间: %d ms)", duration));
        
        List<String> outputList = Reporter.getOutput(result);
        if (!outputList.isEmpty()) {
            for (String output : outputList) {
                test.log(Status.INFO, output);
            }
        }
    }

    @Override
    public void onTestFailure(ITestResult result) {
        long duration = result.getEndMillis() - result.getStartMillis();
        
        List<String> outputList = Reporter.getOutput(result);
        if (!outputList.isEmpty()) {
            test.log(Status.INFO, "<details><summary>测试执行日志</summary>");
            for (String output : outputList) {
                test.log(Status.INFO, output);
            }
            test.log(Status.INFO, "</details>");
        }
        
        test.log(Status.FAIL, String.format("测试失败 (执行时间: %d ms)", duration));
        
        if (result.getThrowable() != null) {
            String errorMessage = result.getThrowable().getMessage();
            String stackTrace = getStackTrace(result.getThrowable());
            
            test.log(Status.FAIL, "<details><summary>错误信息</summary><pre>" + 
                    (errorMessage != null ? errorMessage : "Unknown error") + "</pre></details>");
            test.log(Status.FAIL, "<details><summary>堆栈跟踪</summary><pre>" + stackTrace + "</pre></details>");
        }
    }

    @Override
    public void onTestSkipped(ITestResult result) {
        String skipReason = result.getThrowable() != null ? 
            result.getThrowable().getMessage() : "未知原因";
        test.log(Status.SKIP, "测试跳过: " + skipReason);
        
        if (result.getThrowable() != null) {
            test.log(Status.SKIP, "<details><summary>跳过详情</summary><pre>" + 
                    getStackTrace(result.getThrowable()) + "</pre></details>");
        }
    }

    @Override
    public void onFinish(ITestContext context) {
        extent.flush();
    }
    
    /**
     * 获取异常的堆栈跟踪信息
     * @param throwable 异常对象
     * @return 堆栈跟踪字符串
     */
    private String getStackTrace(Throwable throwable) {
        if (throwable == null) {
            return "";
        }
        
        StringBuilder sb = new StringBuilder();
        sb.append(throwable.getClass().getSimpleName())
          .append(": ")
          .append(throwable.getMessage() != null ? throwable.getMessage() : "")
          .append("\n");
        
        StackTraceElement[] elements = throwable.getStackTrace();
        for (int i = 0; i < Math.min(elements.length, 10); i++) {
            sb.append("    at ").append(elements[i].toString()).append("\n");
        }
        
        if (elements.length > 10) {
            sb.append("    ... ").append(elements.length - 10).append(" more\n");
        }
        
        return sb.toString();
    }
}